using HybridCLR;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using YooAsset;
/// <summary>
/// 用YooAseect下载项目
/// 
/// </summary>
public class LoadDll : MonoBehaviour
{
    /// <summary>
	/// 资源系统运行模式
	/// </summary>
	public EPlayMode PlayMode = EPlayMode.EditorSimulateMode;

    void Start()
    {
        StartCoroutine(StartYooAssect(this.StartGame));
    }
    IEnumerator StartYooAssect(Action onDownloadComplete)
    {
        yield return null;
        // 初始化资源系统
        YooAssets.Initialize();

        // 创建默认的资源包
        var package = YooAssets.CreatePackage("DefaultPackage");
        Debug.Log("设置该资源包为默认的资源包");
        // 设置该资源包为默认的资源包，可以使用YooAssets相关加载接口加载该资源包内容。
        YooAssets.SetDefaultPackage(package);

        if (PlayMode == EPlayMode.EditorSimulateMode)
        {
            var initParameters = new EditorSimulateModeParameters();
            initParameters.SimulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild(EDefaultBuildPipeline.BuiltinBuildPipeline.ToString(), "DefaultPackage");
            yield return package.InitializeAsync(initParameters);
        }
        else if (PlayMode == EPlayMode.OfflinePlayMode)
        {

        }
        else if (PlayMode == EPlayMode.HostPlayMode)
        {
            string defaultHostServer = "http://127.0.0.1/CDN/jiege2/jiegePacke";
            string fallbackHostServer = "http://127.0.0.1/CDN/jiege2/jiegePacke";
            var initParameters = new HostPlayModeParameters();
            initParameters.DecryptionServices = new FileStreamDecryption();
            initParameters.BuildinQueryServices = new GameQueryServices(); //太空战机DEMO的脚本类，详细见StreamingAssetsHelper

            initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
            var initOperation = package.InitializeAsync(initParameters);
            yield return initOperation;

            if (initOperation.Status != EOperationStatus.Succeed)
            {
                Debug.LogError("初始化失败");
                yield break;
            }

            var operation = package.UpdatePackageVersionAsync();
            yield return operation;

            if (operation.Status != EOperationStatus.Succeed)
            {
                //更新失败
                Debug.LogError(operation.Error);
                yield break;
            }
            string packageVersion = operation.PackageVersion;
            Debug.Log($"Updated package Version : {packageVersion}");
            //拿到版本号之后  用版本号获取补丁版本
            Debug.Log("获取补丁版本！");
            // 更新成功后自动保存版本号，作为下次初始化的版本。
            // 也可以通过operation.SavePackageVersion()方法保存。
            bool savePackageVersion = true;
            var operations = package.UpdatePackageManifestAsync(packageVersion);
            yield return operation;

            if (operations.Status != EOperationStatus.Succeed)
            {
                ////更新失败
                //Debug.LogError(operation.Error);
                //yield break;
            }
            //更新成功
            yield return Download();
            //var assets = new List<string>
            //{
            //    //"prefabs",
            //    //"HotUpdate.dll.bytes",
            //    "jiege.dll.bytes",
            //}.Concat(AOTMetaAssemblyFiles);

            //foreach (var asset in assets)
            //{
            //    RawFileHandle handle_jiege = package.LoadRawFileAsync(asset);
            //    yield return handle_jiege;
            //    byte[] fileData = handle_jiege.GetRawFileData();
            //    // string filePath = handle_jiege.GetRawFilePath();
            //    Debug.Log($"dll:{asset}  size:{fileData.Length}");
            //    s_assetDatas[asset] = fileData;

            //}
            //Debug.Log("下载完成！");
            //onDownloadComplete();

        }
        else if (PlayMode == EPlayMode.WebPlayMode)
        {
            string defaultHostServer = "http://127.0.0.1/CDN/jiege2/webJIAGE";
            string fallbackHostServer = "http://127.0.0.1/CDN/jiege2/webJIAGE";
            var createParameters = new WebPlayModeParameters();
            createParameters.DecryptionServices = new FileStreamDecryption();
            createParameters.BuildinQueryServices = new GameQueryServices();
            createParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
            var initOperation = package.InitializeAsync(createParameters);
            yield return initOperation;

            if (initOperation.Status != EOperationStatus.Succeed)
            {
                Debug.LogError("初始化失败");
                yield break;
            }

            var operation = package.UpdatePackageVersionAsync();
            yield return operation;

            if (operation.Status != EOperationStatus.Succeed)
            {
                //更新失败
                Debug.LogError(operation.Error);
                yield break;
            }
            string packageVersion = operation.PackageVersion;
            Debug.Log($"Updated package Version : {packageVersion}");
            //拿到版本号之后  用版本号获取补丁版本
            Debug.Log("获取补丁版本！");
            // 更新成功后自动保存版本号，作为下次初始化的版本。
            // 也可以通过operation.SavePackageVersion()方法保存。
            bool savePackageVersion = true;
            var operations = package.UpdatePackageManifestAsync(packageVersion);
            yield return operation;

            if (operations.Status != EOperationStatus.Succeed)
            {
                //更新失败
                Debug.LogError(operation.Error);
                yield break;
            }
            //更新成功
            yield return Download();

            //下载程序集
            //var assets = new List<string>
            //{
            //    //"prefabs",
            //    //"HotUpdate.dll.bytes",
            //    "jiege.dll.bytes",
            //}.Concat(AOTMetaAssemblyFiles);

            //foreach (var asset in assets)
            //{
            //    RawFileHandle handle_jiege = package.LoadRawFileAsync(asset);
            //    yield return handle_jiege;
            //    byte[] fileData = handle_jiege.GetRawFileData();
            //   // string fileText = handle_jiege.GetRawFileText();
            //    // string filePath = handle_jiege.GetRawFilePath();
            //    Debug.Log($"dll:{asset}  size:{fileData.Length}");
            //    s_assetDatas[asset] = fileData;

            //}
            //onDownloadComplete();
           

            // string fileText = handle_jiege.GetRawFileText();
            // string filePath = handle_jiege.GetRawFilePath();
        }
        string location = "Assets/GameRes/wwise/init.bnk";
        var handle = package.LoadRawFileAsync("jiegeRawFile");
        yield return handle;
        byte[] fileData = handle.GetRawFileData();
        string fileText = handle.GetRawFileText();
        string filePath = handle.GetRawFilePath();
        Debug.LogError("加载问价成功==="+ fileText);
        StartCoroutine(LoadPrefab());
    }
    //IEnumerator LoadPrefab(ResourcePackage package)
    //{
        
    //}
    IEnumerator Download()
    {


        Debug.Log("xiazia ！");
        int downloadingMaxNum = 10;
        int failedTryAgain = 3;
        Debug.Log("xiazia ！1");
        var package = YooAssets.GetPackage("DefaultPackage");
        Debug.Log("xiazia ！2");
        var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);
        Debug.Log("xiazia ！3");
        Debug.Log("需要下载的资源 ！==" + downloader.TotalDownloadCount);
        //没有需要下载的资源
        if (downloader.TotalDownloadCount == 0)
        {
            yield break;
        }

        //需要下载的文件总数和总大小
        int totalDownloadCount = downloader.TotalDownloadCount;
        long totalDownloadBytes = downloader.TotalDownloadBytes;

        //注册回调方法
        downloader.OnDownloadErrorCallback = OnDownloadErrorFunction;
        downloader.OnDownloadProgressCallback = OnDownloadProgressUpdateFunction;
        downloader.OnDownloadOverCallback = OnDownloadOverFunction;
        downloader.OnStartDownloadFileCallback = OnStartDownloadFileFunction;

        //开启下载
        downloader.BeginDownload();
        yield return downloader;

        //检测下载结果
        if (downloader.Status == EOperationStatus.Succeed)
        {
            //下载成功
        }
        else
        {
            //下载失败
        }


    }
    /// <summary>
	/// 更新中
	/// </summary>
	/// <param name="totalDownloadCount"></param>
	/// <param name="currentDownloadCount"></param>
	/// <param name="totalDownloadBytes"></param>
	/// <param name="currentDownloadBytes"></param>
	private void OnDownloadProgressUpdateFunction(int totalDownloadCount, int currentDownloadCount, long totalDownloadBytes, long currentDownloadBytes)
    {
        Debug.Log(string.Format("文件总数：{0}，已下载文件数量{1}，下载总大小：{2}，已下载大小：{3}", totalDownloadCount, currentDownloadCount, totalDownloadBytes, currentDownloadBytes));
    }
    /// <summary>
    /// 开始下载文件
    /// </summary>
    /// <param name="fileName"></param>
    /// <param name="sizeBytes"></param>
    private void OnStartDownloadFileFunction(string fileName, long sizeBytes)
    {
        Debug.Log(string.Format("下载文件：{0}，大小{1}", fileName, sizeBytes));
    }

    /// <summary>
    /// 下载完成
    /// </summary>
    /// <param name="isSucceed"></param>
    private void OnDownloadOverFunction(bool isSucceed)
    {
        Debug.Log(string.Format("下载完成：{0}", isSucceed));
    }
    /// <summary>
    /// 下载错误
    /// </summary>
    /// <param name="fileName"></param>
    /// <param name="error"></param>
    private void OnDownloadErrorFunction(string fileName, string error)
    {
        throw new NotImplementedException();
    }
    private class RemoteServices : IRemoteServices
    {
        private readonly string _defaultHostServer;
        private readonly string _fallbackHostServer;

        public RemoteServices(string defaultHostServer, string fallbackHostServer)
        {
            _defaultHostServer = defaultHostServer;
            _fallbackHostServer = fallbackHostServer;
        }
        string IRemoteServices.GetRemoteMainURL(string fileName)
        {
            return $"{_defaultHostServer}/{fileName}";
        }
        string IRemoteServices.GetRemoteFallbackURL(string fileName)
        {
            return $"{_fallbackHostServer}/{fileName}";
        }
    }
    /// <summary>
	/// 资源文件流加载解密类
	/// </summary>
	private class FileStreamDecryption : IDecryptionServices
    {
        /// <summary>
        /// 同步方式获取解密的资源包对象
        /// 注意：加载流对象在资源包对象释放的时候会自动释放
        /// </summary>
        AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream)
        {
            BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);
            managedStream = bundleStream;
            return AssetBundle.LoadFromStream(bundleStream, fileInfo.ConentCRC, GetManagedReadBufferSize());
        }

        /// <summary>
        /// 异步方式获取解密的资源包对象
        /// 注意：加载流对象在资源包对象释放的时候会自动释放
        /// </summary>
        AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream)
        {
            BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);
            managedStream = bundleStream;
            return AssetBundle.LoadFromStreamAsync(bundleStream, fileInfo.ConentCRC, GetManagedReadBufferSize());
        }

        private static uint GetManagedReadBufferSize()
        {
            return 1024;
        }
    }
    #region download assets

    private static Dictionary<string, byte[]> s_assetDatas = new Dictionary<string, byte[]>();

    public static byte[] ReadBytesFromStreamingAssets(string dllName)
    {
        return s_assetDatas[dllName];
    }

    private string GetWebRequestPath(string asset)
    {
        var path = $"{Application.streamingAssetsPath}/{asset}";
        if (!path.Contains("://"))
        {
            path = "file://" + path;
        }
        return path;
    }
    /// <summary>
    /// 补充元数据的dll的列表
    /// 通过
    /// </summary>
    private static List<string> AOTMetaAssemblyFiles { get; } = new List<string>()
    {
        "mscorlib.dll.bytes",
        "System.dll.bytes",
        "System.Core.dll.bytes",
    };

    IEnumerator DownLoadAssets(Action onDownloadComplete)
    {
        //下载程序集
        var assets = new List<string>
        {
            "prefabs",
            "HotUpdate.dll.bytes",
            "jiege.dll.bytes",
        }.Concat(AOTMetaAssemblyFiles);

        foreach (var asset in assets)
        {
            string dllPath = GetWebRequestPath(asset);
            Debug.Log($"start download asset:{dllPath}");
            UnityWebRequest www = UnityWebRequest.Get(dllPath);
            yield return www.SendWebRequest();

#if UNITY_2020_1_OR_NEWER
            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.Log(www.error);
            }
#else
            if (www.isHttpError || www.isNetworkError)
            {
                Debug.Log(www.error);
            }
#endif
            else
            {
                //下载之后存在字典
                // Or retrieve results as binary data
                byte[] assetData = www.downloadHandler.data;
                Debug.Log($"dll:{asset}  size:{assetData.Length}");
                s_assetDatas[asset] = assetData;
            }
        }
        //下载之后开始游戏
        onDownloadComplete();
    }

    #endregion

    private static Assembly _hotUpdateAss;
    private static Assembly jiegeAss;

    /// <summary>
    /// 为aot assembly加载原始metadata， 这个代码放aot或者热更新都行。
    /// 一旦加载后，如果AOT泛型函数对应native实现不存在，则自动替换为解释模式执行
    /// </summary>
    private static void LoadMetadataForAOTAssemblies()
    {
        /// 注意，补充元数据是给AOT dll补充元数据，而不是给热更新dll补充元数据。
        /// 热更新dll不缺元数据，不需要补充，如果调用LoadMetadataForAOTAssembly会返回错误
        /// 
        HomologousImageMode mode = HomologousImageMode.SuperSet;
        foreach (var aotDllName in AOTMetaAssemblyFiles)
        {
            byte[] dllBytes = ReadBytesFromStreamingAssets(aotDllName);
            // 加载assembly对应的dll，会自动为它hook。一旦aot泛型函数的native函数不存在，用解释器版本代码
            LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
            Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
        }
    }
    /// <summary>
    /// 下载程序集之后开始游戏
    /// </summary>
    void StartGame()
    {
        LoadMetadataForAOTAssemblies();

#if !UNITY_EDITOR
        //System.Reflection.Assembly.Load(ReadBytesFromStreamingAssets("HotUpdate.dll.bytes"));
        System.Reflection.Assembly.Load(ReadBytesFromStreamingAssets("jiege.dll.bytes"));
        //_hotUpdateAss = Assembly.Load(ReadBytesFromStreamingAssets("HotUpdate.dll.bytes"));
        //jiegeAss= Assembly.Load(ReadBytesFromStreamingAssets("jiege.dll.bytes"));
//#else
//        _hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate");
//        jiegeAss=System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "jiege");
#endif
        //Type entryType = _hotUpdateAss.GetType("Entry");
        //entryType.GetMethod("Start").Invoke(null, null);

        ////通过反射直接运行热更新函数
        //Type jiege2 = jiegeAss.GetType("jiegeDemo");
        //jiege2.GetMethod("jiege").Invoke(null, null);
        ////通过反射创造出Delegate后运行
        //Type Hello = _hotUpdateAss.GetType("Hello");
        //MethodInfo method = Hello.GetMethod("Run");

        //Action mainFunc = (Action)Delegate.CreateDelegate(typeof(Action), method);
        //mainFunc();
        StartCoroutine(LoadPrefab());
        //Run_InstantiateComponentByAsset();
    }
    IEnumerator LoadPrefab()
    {
        var package = YooAssets.GetPackage("DefaultPackage");

        AssetHandle handle = package.LoadAssetAsync<GameObject>("tanghulu");
        yield return handle;
        GameObject go = handle.InstantiateSync();
        Debug.Log($"Prefab name is {go.name}");
        go.transform.SetParent(GameObject.Find("Canvas").transform);
        go.transform.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, 0);
        go.transform.GetComponent<RectTransform>().offsetMax = Vector2.zero;
        //go.transform.GetComponent<RectTransform>().sizeDelta = Vector2.one;
        //go.transform.GetComponent<RectTransform>()sizeDelta = new Vector2(panelWidth, panelHeight);

    }
    /// <summary>
    /// ab实例化  还原身上的热更新代码  首先代码必须要加载出来之后
    /// </summary>
    private static void Run_InstantiateComponentByAsset()
    {
        // 通过实例化assetbundle中的资源，还原资源上的热更新脚本
        AssetBundle ab = AssetBundle.LoadFromMemory(LoadDll.ReadBytesFromStreamingAssets("prefabs"));
        GameObject cube = ab.LoadAsset<GameObject>("Cube");
        GameObject.Instantiate(cube);
        GameObject jiege = ab.LoadAsset<GameObject>("Image_jiege");
        GameObject.Instantiate(jiege,GameObject.Find("Canvas").transform);

    }
}
